package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	_ "strings"
)

// ==========文件==========
// 文件在程序中是以流的形式来操作的
// 流: 数据在数据源(文件)和程序(内存)之间经历的路径
// 输入流: 数据从数据源(文件)到程序(内存)的路径
// 输出流: 数据从程序(内存)到数据源(文件)的路径

// 带缓冲的方式读取文件
func BufferReadString(name string) {
	// Open(name string) (*File, error)返回一个文件指针和error
	// 如果文件存在, 则file类似于&{0xc000048180}、err等于nil
	// 如果文件不存在, 则file等于nil、err返回*PathError
	file, err := os.Open(name) // 文件模式: O_RDONLY
	if file == nil {
		fmt.Println(err)
	}
	// NewReader(rd io.Reader) *Reader
	// NewReader接收一个io.Reader类型的变量
	// 而io包里的Reader是一个接口, 它定义里一个方法Read
	// type Reader interface {
	//     Read(p []byte) (n int, err error)
	// }
	// 因为File结构体实现里Reader接口的Read方法, 因此可以将*File类型变量传给NewReader
	// (f *File) Read(b []byte) (n int, err error)

	/*
			ReadString弊端: 文本内容1中4的下一行没有内容, 则4这一行也读取不到
		                   文本内容2中4这一行下面还有内容, 则能读到4

			文本内容1:
			1
			2
			3
			4

			文本内容2:
			1
			2
			3
			4

	*/

	reader := bufio.NewReader(file) // 带缓冲的reader, 默认缓冲4096字节
	for {
		// func (b *Reader) ReadString(delim byte) (string, error)
		str, err := reader.ReadString('\n') // 读到一行
		if err == io.EOF {
			break
		}
		fmt.Print(str)
	}

	defer file.Close()
}

// 带缓冲的方式读取文件: 不推荐
// 当一行的文本很长的时候, 需再次调用才可以读取出一行的部分数据
func BufferScannerText(name string) {
	file, err := os.Open(name)
	if err != nil {
		fmt.Println(err)
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	fmt.Println("第二次调用")
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	fmt.Println("第二次调用也只读出一行的部分数据")
}

// 带缓冲的方式读取文件: 推荐
func BufferReadLine(name string) {
	file, _ := os.Open(name)
	reader := bufio.NewReader(file)
	for {
		line, _, e := reader.ReadLine()
		if e == io.EOF {
			break
		}
		fmt.Println(string(line))
	}
}

// 一次性读取文件, 适用于小文件
func ReadAll(name string) error {
	// ReadFile(filename string) ([]byte, error)
	bytes, err := ioutil.ReadFile(name) // ReadFile内部封装好了打开和关闭, 不需要显示手动打开和关闭
	if err != nil {
		return err
	}

	/*
		文本内容:
		a
		b
		c d
		你
	*/
	// 文本一共有4行, 每一行最后都有一个换行符, 最后一行`你`后面没有换行符, 因为`你`下一行没有数据
	// a:97、b:98、c:99、d:100、空格:32、换行符:10、你:228 189 160
	fmt.Println(bytes) // [97 10 98 10 99 32 100 10 228 189 160]

	/*
		文本内容:
		a
		b
		c d
		你

	*/
	// 文本一共有5行, 每一行最后都有一个换行符, 最后一行为空行
	// a:97、b:98、c:99、d:100、空格:32、换行符:10、你:228 189 160
	fmt.Println(bytes) // [97 10 98 10 99 32 100 10 228 189 160 10]

	/*
		文本内容:
		a
		b
		c\nd
		你
		好
	*/
	// 文本一共有5行, 每一行最后都有一个换行符, 最后一行`好`后面没有换行符, 因为`好`下一行没有数据
	// 第三行中间的\n代表是\和n, 它不是一个整体, 不代表换行符
	// a:97、b:98、c:99、d:100、空格:32、换行符:10、\:92、n:110、你:228 189 160、好:229 165 189
	fmt.Println(bytes) // [97 10 98 10 99 92 110 100 10 228 189 160 10 229 165 189]

	stringByte := string(bytes) // 将[]byte切片转为字符串
	// 1、以字节方式遍历字符串, 无法解决中文的问题
	//for i := 0; i < len(stringByte); i++ {
	//	fmt.Printf("码值:%d对应的字符:%c\n", stringByte[i], stringByte[i])
	//}
	/*
		码值:97对应的字符:a
		码值:10对应的字符:

		码值:98对应的字符:b
		码值:10对应的字符:

		码值:99对应的字符:c
		码值:92对应的字符:\
		码值:110对应的字符:n
		码值:100对应的字符:d
		码值:10对应的字符:

		码值:228对应的字符:ä
		码值:189对应的字符:½
		码值:160对应的字符:
		码值:10对应的字符:

		码值:229对应的字符:å
		码值:165对应的字符:¥
		码值:189对应的字符:½

	*/

	// 2、以Unicode字符遍历字符串, 即for-range方式遍历, 如果有中文, 则索引不连续
	//for idx, v := range stringByte {
	//	fmt.Printf("idx:%d对应的字符:%c\n", idx, v)
	//}
	/*
		idx:0对应的字符:a
		idx:1对应的字符:

		idx:2对应的字符:b
		idx:3对应的字符:

		idx:4对应的字符:c
		idx:5对应的字符:\
		idx:6对应的字符:n
		idx:7对应的字符:d
		idx:8对应的字符:

		idx:9对应的字符:你
		idx:12对应的字符:

		idx:13对应的字符:好
	*/

	// 3、将字符串转换成[]rune切片
	for idx, v := range []rune(stringByte) {
		fmt.Printf("~idx:%d对应的字符:%c\n", idx, v)
	}
	/*
		~idx:0对应的字符:a
		~idx:1对应的字符:

		~idx:2对应的字符:b
		~idx:3对应的字符:

		~idx:4对应的字符:c
		~idx:5对应的字符:\
		~idx:6对应的字符:n
		~idx:7对应的字符:d
		~idx:8对应的字符:

		~idx:9对应的字符:你
		~idx:10对应的字符:

		~idx:11对应的字符:好
	*/

	// 可以用fmt.Sprintf("%c", v)将一个Unicode码值转换成中文汉字, v是一个Unicode码值

	return nil
}

func main() {
	BufferReadString("abc.txt.read")
	BufferScannerText("abc.txt.read")
	BufferReadLine("abc.txt.read")
}
